package com.ls.io.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoop;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.*;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.ThreadExecutorMap;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class NettyTest {

    @Test
    public void byteBuf(){
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(10, 20);
        printByteBuf(byteBuf);

        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
        printByteBuf(byteBuf);

        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
        printByteBuf(byteBuf);

        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
        printByteBuf(byteBuf);

        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
        printByteBuf(byteBuf);

        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
        printByteBuf(byteBuf);

//        byteBuf.writeBytes(new byte[]{1, 2, 3, 4});
//        printByteBuf(byteBuf);
    }

    private void printByteBuf(ByteBuf byteBuf){
        System.out.println("isReadable   :" + byteBuf.isReadable());
        System.out.println("readerIndex  :" + byteBuf.readerIndex());
        System.out.println("readableBytes:" + byteBuf.readableBytes());
        System.out.println("isWritable   :" + byteBuf.isWritable());
        System.out.println("writerIndex  :" + byteBuf.writerIndex());
        System.out.println("writableBytes:" + byteBuf.writableBytes());
        System.out.println("capacity     :" + byteBuf.capacity());
        System.out.println("maxCapacity  :" + byteBuf.maxCapacity());

        System.out.println("------------------------------------");
    }

    @Test
    public void executeLoop() throws IOException, InterruptedException {
        NioEventLoopGroup loopGroup = new NioEventLoopGroup(2);

        loopGroup.execute(() ->{
            while(true){
                try {
                    System.out.println("hello001");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        TimeUnit.SECONDS.sleep(20);
    }

    @Test
    public void ThreadPerTaskExecutor() throws Exception {
        ThreadPerTaskExecutor executor = new ThreadPerTaskExecutor(new MyThreadFactory());

        for(int i = 0; i < 10; i++){
            executor.execute(() ->{
                try {
                    System.out.println(Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        TimeUnit.SECONDS.sleep(20);
    }



    @Test
    public void executeLoopGroup() throws IOException {
        NioEventLoopGroup loopGroup = new NioEventLoopGroup(1, new MyThreadFactory());

        for(int i = 0; i < 10; i++){
            loopGroup.execute(() ->{
                try {
                    System.out.println(Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        System.in.read();
    }

    @Test
    public void clientMode() throws IOException, InterruptedException {
        NioEventLoopGroup loopGroup = new NioEventLoopGroup(1);

        NioSocketChannel client = new NioSocketChannel();
        loopGroup.register(client);

        ChannelFuture connect = client.connect(new InetSocketAddress("192.168.199.128", 9000));

        ChannelPipeline pipeline = client.pipeline();
        pipeline.addLast(new MyHandler());

        connect.sync();

        ByteBuf buf = Unpooled.copiedBuffer("hello server".getBytes());
        client.writeAndFlush(buf);

        connect.channel().closeFuture().sync();
    }


    @Test
    public void serverMode() throws IOException, InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);

        NioServerSocketChannel server = new NioServerSocketChannel();
        group.register(server);

        ChannelPipeline pipeline = server.pipeline();
        pipeline.addLast(new MyAcceptHandler(group));

        ChannelFuture bind = server.bind(new InetSocketAddress(8088));
        bind.sync().channel().closeFuture().sync();

    }

    @Test
    public void nettyClient() throws InterruptedException {
        Bootstrap bs = new Bootstrap();
        ChannelFuture connect = bs.group(new NioEventLoopGroup(1))
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new MyHandler());
                    }
                })
                .connect(new InetSocketAddress("127.0.0.1", 8080));

        connect.sync();

        NioSocketChannel client = (NioSocketChannel) connect.channel();
        ByteBuf buf = Unpooled.copiedBuffer("hello server".getBytes());
        client.writeAndFlush(buf);


        client.closeFuture().sync();
    }

    @Test
    public void nettyClient2() throws InterruptedException {
        Bootstrap bs = new Bootstrap();
        ChannelFuture connect = bs.group(new NioEventLoopGroup(1))
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                CharSequence str =
                                        buf.getCharSequence(0, buf.readableBytes(), Charset.forName("utf-8"));
                                System.out.println("client read:" + str);
                            }
                        });
                    }
                })
                .connect(new InetSocketAddress("127.0.0.1", 8088));

        connect.sync();

        NioSocketChannel client = (NioSocketChannel) connect.channel();
        ByteBuf buf = Unpooled.copiedBuffer("hello 阿斯顿飞凯撒的减肥撒地方卡拉斯加阿斯顿两份健康阿斯兰看到房间阿斯顿快乐减肥阿斯顿两款减肥卢卡斯的经费卡拉斯的减肥了空间阿斯顿离开房间来思考的减肥了空间aaaa".getBytes());
        client.writeAndFlush(buf);

        Thread.sleep(10000);

        buf = Unpooled.copiedBuffer("bbbb".getBytes());
        client.writeAndFlush(buf);

        Thread.sleep(10000);

        buf = Unpooled.copiedBuffer("flush".getBytes());
        client.writeAndFlush(buf);


        client.closeFuture().sync();
    }

    @Test
    public void nettyServer() throws InterruptedException {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup work = boss;

        ServerBootstrap bs = new ServerBootstrap();
        ChannelFuture bind = bs.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(100, 200))
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new MyHandler());
                    }
                })
                .bind(8088);

        bind.sync().channel().closeFuture().sync();
    }


}

class MyAcceptHandler extends ChannelInboundHandlerAdapter{

    EventLoopGroup eventLoopGroup;

    public MyAcceptHandler(EventLoopGroup eventLoopGroup) {
        this.eventLoopGroup = eventLoopGroup;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NioSocketChannel client = (NioSocketChannel) msg;

        client.pipeline().addLast(new MyHandler());

        eventLoopGroup.register(client);
    }

}

class MyHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client register");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client active");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        ChannelPipeline pipeline = ctx.pipeline();
//        Map<String, ChannelHandler> stringChannelHandlerMap = pipeline.toMap();
//        System.out.println(stringChannelHandlerMap);

        ByteBuf buf = (ByteBuf) msg;
        CharSequence str =
                buf.getCharSequence(0, buf.readableBytes(), Charset.forName("utf-8"));
        System.out.println("read:" + str);

        if(str.toString().startsWith("flush")){
            ctx.writeAndFlush(buf);
        }else{
            ctx.write(buf);
        }
    }


    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

}